#include "grafo.h"

Grafo::Grafo()
{
    inizializza();
}

Grafo::~Grafo()
{
    //dtor
}

void Grafo::inizializza()
{
    indice_corrente = 0;
    g_slist_free(nodi);
    g_slist_free(lista_adiacenze);
    nodi = NULL;
    lista_adiacenze = NULL;
    selezionato = NULL;
    arco_selezionato = NULL;
}

Nodo* Grafo::get_selezionato()
{
    return selezionato;
}

Elemento_ui* Grafo::find_by_xy(gdouble x, gdouble y)
{
    GSList* temp = nodi;
    
    while(temp != NULL)
    {
        Nodo* nodo_corrente = ((Nodo *) temp->data);
        if(nodo_corrente->intersect(x, y))
            return nodo_corrente;         
        
        temp = g_slist_next(temp);
    }
    
    temp = lista_adiacenze;
    
    while(temp != NULL)
    {
        Arco* arco_corrente = ((Arco*) temp->data);
        if(arco_corrente->intersect(x, y))
            return arco_corrente;
            
            temp = g_slist_next(temp);
    }

    return NULL;    
}

void Grafo::seleziona(Nodo* nodo)
{
    assert(nodo != NULL);
    deseleziona_nodi();
    nodo->set_selected(TRUE);
    selezionato = nodo;
}

void Grafo::seleziona(gdouble x, gdouble y)
{
    Elemento_ui* selezionato = find_by_xy(x, y);
    
    Nodo* nodo_corrente = dynamic_cast<Nodo*>(selezionato); 
    if(nodo_corrente != NULL)
        seleziona(nodo_corrente);
        
    Arco* arco_corrente = dynamic_cast<Arco*>(selezionato);
    arco_selezionato = arco_corrente;
}

Nodo* Grafo::find_by_index(guint indice)
{
    GSList* temp = nodi;

    while(temp != NULL)
    {
    	Nodo* nodo_corrente = (Nodo *) temp->data;
           if(nodo_corrente->get_indice() == indice)
               return nodo_corrente;
	    temp = g_slist_next(temp);
    }

    return NULL;
}

void Grafo::deseleziona_nodi()
{
   GSList* temp = nodi;

    while(temp != NULL)
    {
    	((Nodo *) temp->data)->set_selected(FALSE);
    	temp = g_slist_next(temp);
    }
}

Nodo* Grafo::aggiungi_nodo(gdouble x, gdouble y)
{
    Nodo* nuovo_nodo = new Nodo(x, y, indice_corrente++);
    Nodo* nodo_selezionato = get_selezionato();
    deseleziona_nodi();
    nuovo_nodo->set_selected(TRUE);
    selezionato = nuovo_nodo;
    nodi = g_slist_append(nodi, nuovo_nodo);

    if(nodo_selezionato != NULL)
    {
        Arco* nuovo_arco = new Arco(nodo_selezionato, nuovo_nodo, 1);
        lista_adiacenze = g_slist_append(lista_adiacenze, nuovo_arco);        
    }

    return nuovo_nodo;
}

void Grafo::aggiungi_arco(Nodo* destinazione)
{
    assert(destinazione != NULL);
    
    // La creazione dell'arco può avvenire solo se abbiamo un nodo di origine e se il nodo di destinazione selezionato non coincide l'origine.
    Nodo* origine = get_selezionato();
    if(origine != NULL && origine->get_indice() != destinazione->get_indice())
    {
        Arco* nuovo_arco = new Arco(origine, destinazione, 1);
        lista_adiacenze = g_slist_append(lista_adiacenze, nuovo_arco);   
        seleziona(destinazione);     
    }
}


void Grafo::prepara()
{
    GSList* temp = nodi;

    while(temp != NULL)
    {
	Nodo* nodo_corrente = (Nodo *) temp->data;

    // Utilizziamo sempre il nodo selezionato come origine
	if(nodo_corrente->get_indice() == selezionato->get_indice())
		nodo_corrente->set_distanza(0);
	else
        nodo_corrente->set_distanza_infinita();

	nodo_corrente->set_precedente(NULL);

	temp = g_slist_next(temp);
    }
}

guint Grafo::get_numero_nodi()
{
	return g_slist_length(nodi);
}

void Grafo::draw(cairo_t* &cr)
{
    GSList* temp = nodi;
    
    while(temp != NULL)
    {
        ((Nodo *) temp->data)->draw(cr);
        
        temp = g_slist_next(temp);
    }
    
    temp = lista_adiacenze;
    
    while(temp != NULL)
    {
        ((Arco *) temp->data)->draw(cr);
        
        temp = g_slist_next(temp);
    }
}

void Grafo::lmb_premuto(gdouble x, gdouble y)
{
    is_lmb_pressed = TRUE;
    mouse_origin_x = x;
    mouse_origin_y = y;
    
    seleziona(x, y);    
}

void Grafo::lmb_rilasciato()
{
    is_lmb_pressed = FALSE;
}

gboolean Grafo::is_dragging()
{
    if(is_lmb_pressed)
    {
        gdouble delta_x = mouse_origin_x - x;
        gdouble delta_y = mouse_origin_y - y;
        return (delta_x > 10 || delta_x < -10 || delta_y > 10 || delta_y < -10);
    }
    return FALSE;
}

void Grafo::update(gdouble x, gdouble y)
{
    if(is_dragging())
    {
        if(arco_selezionato != NULL)
        {
            arco_selezionato->set_lunghezza(mouse_origin_y - y);
        } else
        {
            Nodo* selezionato = get_selezionato();
                
            if(selezionato != NULL)
                selezionato->update(x, y);
        }
    } else
    {
        GSList* temp = nodi;
        
        while(temp != NULL)
        {
            ((Nodo *) temp->data)->set_over(x, y);
            
            temp = g_slist_next(temp);
        }
    
        temp = lista_adiacenze;
        
        while(temp != NULL)
        {
            ((Arco *) temp->data)->set_over(x, y);
            
            temp = g_slist_next(temp);
        }
    } 
}

void Grafo::salva(const char* file)
{
    ofstream of;
    of.open(file);

    GSList* temp = get_nodi();    
    while(temp != NULL)
    {
        Nodo* nodo_corrente = ((Nodo *) temp->data);
        
        of<<"N"<<endl;
        of<<nodo_corrente->get_indice()<<endl;
        of<<nodo_corrente->get_x()<<endl;
        of<<nodo_corrente->get_y()<<endl;
        
        temp = g_slist_next(temp);
    }
    
    temp = get_archi();
    while(temp != NULL)
    {
        Arco* arco_corrente = ((Arco *) temp->data);
        
        of<<"A"<<endl;
        of<<arco_corrente->get_origine()->get_indice()<<endl;
        of<<arco_corrente->get_destinazione()->get_indice()<<endl;
        of<<arco_corrente->get_lunghezza()<<endl;
        
        temp = g_slist_next(temp);
    }    
    
    if(selezionato != NULL)
        of<<"S"<<endl;
        of<<selezionato->get_indice();
    
    of.close();
}

void Grafo::carica_elemento(istream &ifs)
{
    char* tipo = new char[1];
    ifs>>tipo;
    
    if(tipo[0] == 'N')
    {
        gdouble x;
        gdouble y;
        guint indice;
        
        ifs>>indice;
        ifs>>x;
        ifs>>y;
        Nodo* nuovo_nodo = new Nodo(x, y, indice);
        nodi = g_slist_append(nodi, nuovo_nodo);
        
        if(indice > indice_corrente)
            indice_corrente = indice;
    }	
    if(tipo[0] == 'A')
    {
        guint indice_precedente;
        guint indice_successivo;
        guint lunghezza;
        
        ifs>>indice_precedente;
        ifs>>indice_successivo;
        ifs>>lunghezza;
        
        Nodo* origine = find_by_index(indice_precedente);
        Nodo* destinazione = find_by_index(indice_successivo);
        Arco* nuovo_arco = new Arco(origine, destinazione, lunghezza);
        lista_adiacenze = g_slist_append(lista_adiacenze, nuovo_arco);
    }
    
    if(tipo[0] == 'S')
    {
        guint indice_selezionato;
        ifs>>indice_selezionato;
        
        Nodo* selezionato = find_by_index(indice_selezionato);
        seleziona(selezionato);
    }
    
    delete tipo;
}

void Grafo::carica(const char* file)
{
    inizializza();
    
    ifstream ifs;
    ifs.open(file);
 
    while(ifs)
        carica_elemento(ifs);
    
    indice_corrente++;
    ifs.close();
}
